#!/usr/bin/env python
# -*- coding:utf-8 -*-
'''
@File    :   learn_Module.py
@Time    :   2025/01/11 20:32:11
@Author  :   xiangyu Chen
@Version :   1.0
@Contact :   yu112yu@sina.com
@License :   (C)Copyright 2017-2018, Liugroup-NLPR-CASIA
@python_requires    :   None
@Desc    :   在 Python 中，一个.py 文件就称之为一个模块（Module）。
    一个模块中可以包含多个函数，类，和变量。
    一个模块可以被多个 Python 脚本引用.
    引入模块可以用 import 语句，或者 from...import 语句。
    以下是一些示例：
    引入一个模块：
    import my_module
    my_module.function1()
    my_module.function2()
    引入一个模块的部分函数：
    from my_module import function1, function2
    function1()
    function2()
    引入一个模块的部分类：
    from my_module import Class1, Class2
    obj1 = Class1()
    obj2 = Class2()
    引入一个模块的全部函数和类：
    from my_module import *
    function1()
    function2()
    obj1 = Class1()
    obj2 = Class2()
    https://docs.python.org/3/library/functions.html
'''

import sys
# 每一个包目录下面都会有一个__init__.py 的文件
# __init__.py 可以是空文件，也可以有 Python 代码，因为__init__.py 本身就是一个模块，而它的模块名就是 mycompany。
import types

import learn_argument

print(__name__)

# =================作用域===============
# 函数和变量我们希望仅仅在模块内部使用。在Python 中，是通过_前缀来标识。但这只是一种约定，Python解释器不会强制执行这一点，但它提醒开发者这个变量或方法是私有的。

print('learn_argument._private_var:', learn_argument._private_var)
print('learn_argument.public_var: ', learn_argument.public_var)
print('learn_argument.__private_var:', learn_argument.__private_var)
# 外部不需要引用的函数全部定义成 private，只有外部需要引用的函数才定义为 public。


def _private_1(name):
    return 'Hello, %s' % name


def _private_2(name):
    return 'Hi, %s' % name


def greeting(name):
    if len(name) > 3:
        return _private_1(name)
    else:
        return _private_2(name)

# 模块搜索路径
# 搜索路径存放在 sys 模块的 path 变量中.


# print('Python 解释器的搜索路径：', sys.path)


# 添加自己的搜索目录.
# 一是直接修改 sys.path
# sys.path.append('/path/to/my/module')
# 第二种方法是设置环境变量 PYTHONPATH

# __init__函数是Python中一个特殊的函数，它在创建对象时自动执行，用于初始化对象的属性.

class Student(object):
    def __init__(self, name, score, sex):
        self.name = name
        self.score = score
        self.__sex = sex

    # 类函数和普通的函数相比，在类中定义的函数只有一点不同，就是第一个参数永远是实例变量 self，并且，调用时，不用传递该参数。
    def print_score(self):
        print('%s: %s' % (self.name, self.score))

    def get_sex(self):
        return self.__sex

    def set_sex(self, sex):
        if sex in ['M', 'F']:
            self.__sex = sex
        else:
            print('Sex must be M or F.')


bart = Student('Bart Simpson', 59, 'M')


bart.print_score()

# Python 允许对实例变量绑定任何数据
bart.age = 8
print('类中未定义的变量 bart.age:', bart.age)
try:
    print('实例的变量名如果以__开头，就变成了一个私有变量（private），只有内部可以访问，外部不能访问， ', bart.__sex)
    # 私有变量在Python中是以__开头的
    # 私有变量的特点是，无法在外部被直接访问，但是可以通过添加 getter和setter方法来访问。
    # 但是，在Python中，并没有完全的私有属性和私有方法的实现，只是一种 convention。
    # 私有变量在 Python 2.x中可以用 _classname__variablename来表示，在 Python 3.x中，不再推荐这么用。
    # 以双下划线开头，并且以双下划线结尾的，是特殊变量，特殊变量是可以直接访问的，不是 private 变量.
except AttributeError as e:
    print('AttributeError:', e)

bart.__sex = 'F'
print('实例的私有变量无法直接修改，bart.__sex = \'F\'：', bart.get_sex())

print('使用getter方法访问，bart.get_sex():', bart.get_sex())
bart.set_sex('F')
print('使用setter方法访问，bart.set_sex(\'F\')后再get: ', bart.get_sex())

print('实际也可以直接访问变量，不能访问是因为 Python 解释器对外把_ 变量改成了“_类名__变量名”，bart._Student__sex：',
      bart._Student__sex)


# 继承和多态。

class Animal(object):
    def run(self):
        print('Animal is running...')


animal = Animal()


class Dog(Animal):
    def run(self):
        print('Dog is running...')
        super().run()  # 调用父类的方法
        print('Dog is more running...')
        # 多态的特点是，在运行时，会根据对象的类型来决定调用哪个类的方法。


dog = Dog()

dog.run()

# 多态的好处：
# 1. 代码复用，编写更少的代码。
# 2. 让程序更灵活，可以实现更多的功能。
print('一个实例的数据类型是某个子类，那它的数据类型也可以被看做是父类,isinstance(dog,Animal): ',
      isinstance(dog, Animal))
print('但是，反过来就不行,isinstance(animal,Dog): ', isinstance(animal, Dog))
# 多态的弊端：
# 1. 让程序的可读性和可维护性变差，因为有了多态，父类的方法和子类的方法在使用时需要进行显式转换。
# 2. 让代码的性能变差，因为有了多态，Python 解释器需要进行多次 type() 和 isinstance()  checks。


# 获取对象信息
# 判断一个对象是否是函数怎么办？可以使用 types 模块中定义的常量：
def fn(obj):
    pass


print('type(fn) == types.FunctionType : ',
      type(fn) == types.FunctionType)

# 对于 class 的继承关系来说，使用 type()就很不方便。我们要判断 class的类型，可以使用 isinstance()函数。
print('isinstance(dog.run, types.FunctionType): ',
      isinstance(dog.run, types.FunctionType))
print('isinstance(dog.run, types.BuiltinFunctionType): ',
      isinstance(dog.run, types.BuiltinFunctionType))
print('isinstance(dog.run, types.MethodType): ',
      isinstance(dog.run, types.MethodType))
print('isinstance([1, 2, 3], (list, tuple,str)): ',
      isinstance([1, 2, 3], (list, tuple, str)))

# 获取对象的所有属性和方法 dir()函数

print('dir(dog): ', dir(dog))

# 对象的属性和方法可以直接被修改，这就是动态语言的特点。

dog.name = 'Dog'
print('dog.name: ', dog.name)

del dog.name
try:
    print('dog.name after del: ', dog.name)
except AttributeError as e:
    print('dog.name after del: ', 'AttributeError:', e)

# 在 Python 中，如果你调用 len()函数试图获取一个对象的长度，实际上，在 len()函数内部，它自动去调用该对象的__len__()方法，所以，下面的代码是等价的：

print('len(\'ABC\')等价于\'ABC\'.__len__() : ', len('ABC'), 'ABC'.__len__())


class HB():
    def __len__(self):
        return 5


hb = HB()
print('自己写__len__ , len(hb) : ', len(hb))
# 我们在编写自己的类时，如果没有特别指定，我们的类默认继承 object 类。
print('isinstance(hb,object): ', isinstance(hb, object))

print("hasattr(obj, 'y') # 有属性'y'吗？", hasattr(hb, 'y'))
print("setattr(obj, 'y', 19) # 设置一个属性'y'", setattr(hb, 'y', 19))
print("hasattr(obj, 'y') # 有属性'y'吗？", hasattr(hb, 'y'))
print("getattr(obj, 'y') # 获取属性'y'", getattr(hb, 'y'))
print("obj.y # 获取属性'y'", hb.y)
